1. fseek()을 이용한 파일 포인터(오프셋) 위치 제어
-
fseek()함수는 파일 내부에서 다음에 읽거나 쓸 위치를 가리키는 파일 오프셋(File Offset)을 원하는 지점으로 이동시킵니다. -
SEEK_SET(0): 파일의 시작(처음)을 기준으로 오프셋 이동 (offset >= 0) -
SEEK_CUR(1): 파일의 현재 읽기/쓰기 위치를 기준으로 상대적 이동 -
SEEK_END(2): 파일의 끝(EOF)을 기준으로 역방향 이동 (offset <= 0) -
[핵심 개념 구분]
- 파일 포인터 (File Offset Pointer): 파일 내부에서 읽기/쓰기가 일어날 바이트 위치를 가리키는 정수형 커서입니다. (
fseek으로 제어) FILE *(스트림 구조체 포인터): 유저 공간 힙 메모리에 할당된 파일 제어 구조체(버퍼, OS 핸들, 상태 플래그 등)의 메모리 주소값입니다.
- 파일 포인터 (File Offset Pointer): 파일 내부에서 읽기/쓰기가 일어날 바이트 위치를 가리키는 정수형 커서입니다. (
-
파일 크기 측정 및 원복 패턴:
FILE *fp = fopen("data.bin", "rb");
if (fp != NULL) {
fseek(fp, 0, SEEK_END); // 포인터를 파일 맨 끝으로 이동
long file_size = ftell(fp); // 시작점부터 끝점까지의 총 바이트 수 반환
rewind(fp); // fseek(fp, 0, SEEK_SET)과 동일 (시작 위치로 원복)
printf("파일 총 크기: %ld bytes\n", file_size);
fclose(fp);
}
- 대용량 파일 주의: 2GB 이상의 대용량 파일을 다룰 때는
long(32비트 환경 2GB 제한) 대신 64비트 오프셋 함수(_fseeki64/fseeko,off_t)를 사용해야 오버플로우가 발생하지 않습니다.
2. fopen()과 fclose() 1:1 라이프사이클
-
fopen()을 호출하면 유저 메모리(FILE구조체 및 4KB 버퍼)와 커널 리소스(File Descriptor / Handle / File Lock)가 동시에 할당됩니다. -
따라서 사용이 끝나면 반드시
fclose()를 호출하여 자원을 반납해야 합니다.fopen()호출 시: [User]FILE구조체 + 4KB 버퍼 할당 / [Kernel] FD 테이블 인덱스 부여 & File Lock 적용fclose()호출 시: 유저 버퍼 Flush 메모리 free OS FD 반환 및 Lock 해제
3. fclose() 누락 시 발생하는 4대 장애
-
파일 디스크립터 누수 (FD Leak):
- OS가 프로세스당 열 수 있는 파일 핸들 제한(
ulimit -n, 보통 1024개)을 초과하게 됩니다. EMFILE (Too many open files)에러가 발생하며 이후 새로운 파일 오픈 및 네트워크 소켓 통신이 전부 차단됩니다.
- OS가 프로세스당 열 수 있는 파일 핸들 제한(
-
메모리 누수 (Memory Leak):
- 스트림 제어 블록과 4KB 버퍼 메모리가 힙에 잔류하여 시스템 메모리를 낭비합니다.
-
데이터 유실 (버퍼 미반영):
- 4KB 버퍼에 남아 있던 데이터가 디스크로 완전히 내려쓰이지(Flush) 않은 상태에서 프로세스가 종료되면 기록 중이던 데이터가 증발합니다.
-
파일 락(Lock) 지속:
- OS 레벨의 파일 잠금이 풀리지 않아 외부 프로그램이나 다른 스레드에서 해당 파일을 열거나 수정·삭제할 수 없게 됩니다.
4. 파일 읽기 반복문 안티패턴 (feof 오용 방지)
feof()함수는 EOF에 도달했을 때 즉시 참이 되는 것이 아니라, EOF를 넘어서 읽기를 시도했다가 실패한 후에 플래그가 설정됩니다.- 따라서
while (!feof(fp))형태로 작성하면 루프가 한 번 더 실행되어 마지막 데이터가 2번 처리되는 버그가 발생합니다.
// [잘못된 패턴] feof는 읽기 실패 후 세팅되므로 마지막 데이터가 2번 중복 처리됨
while (!feof(fp)) {
fgets(buf, sizeof(buf), fp);
printf("%s", buf);
}
// [올바른 패턴] 읽기 함수의 반환값(NULL 또는 읽은 바이트 수)으로 루프 조건 검증
while (fgets(buf, sizeof(buf), fp) != NULL) {
printf("%s", buf);
}